/*******************************************************************************
* Copyright 2005-2007, CHISEL Group, University of Victoria, Victoria, BC, Canada.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* The Chisel Group, University of Victoria
*******************************************************************************/
package ca.uvic.chisel.diver.sequencediagrams.sc.java.editors;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.DoStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.WhileStatement;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.zest.custom.uml.viewers.IMessageGrouper;
import org.eclipse.zest.custom.uml.viewers.IMessageGrouping;
import org.eclipse.zest.custom.uml.viewers.MessageGrouping;
import org.eclipse.zest.custom.uml.viewers.UMLSequenceViewer;
import ca.uvic.chisel.javasketch.internal.ast.groups.ASTMessageGroupingTree;
import ca.uvic.chisel.javasketch.ui.ISketchColorConstants;
import ca.uvic.chisel.javasketch.ui.internal.presentation.ASTMessageGrouper.ASTMessageGrouping;
/**
* Uses the AST of the java model to discover groups for the passed messages.
* @author Del Myers
*/
public class JavaMessageGrouper implements IMessageGrouper {
private static class MappedMessageGrouping extends MessageGrouping {
private Object key;
/**
* @param activationElement
* @param offset
* @param length
* @param name
*/
public MappedMessageGrouping(Object activationElement, int offset,
int length, String name, Object key) {
super(activationElement, offset, length, name);
this.key = key;
}
public Object getKey() {
return key;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return getName() + "[" + getOffset() + "," + (getOffset()+getLength()) +"]";
}
}
public IMessageGrouping[] calculateGroups2(UMLSequenceViewer viewer,
Object activationElement, Object[] children) {
HashMap<ASTNode, MappedMessageGrouping> groups = new HashMap<ASTNode, MappedMessageGrouping>();
if (!(activationElement instanceof IAdaptable)) {
return new IMessageGrouping[0];
}
ASTNode activationNode = (ASTNode)((IAdaptable)activationElement).getAdapter(ASTNode.class);
if (!(activationNode instanceof MethodDeclaration)) {
return new IMessageGrouping[0];
}
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof IAdaptable) {
ASTNode messageNode = (ASTNode)((IAdaptable)children[i]).getAdapter(ASTNode.class);
if (messageNode != null) {
ASTNode currentParent = messageNode.getParent();
while (currentParent != null && currentParent != activationNode) {
ASTNode block = null;
String text = null;
Color c = null;
Color bc = null;
String expressionString = "";
switch (currentParent.getNodeType()) {
case ASTNode.IF_STATEMENT:
block = checkIfSide((IfStatement)currentParent, messageNode);
if (block != null && block == ((IfStatement)currentParent).getElseStatement()) {
text = "else";
} else if (block == ((IfStatement)currentParent).getThenStatement()) {
text = "if (" + ((IfStatement)currentParent).getExpression().toString() + ")";
}
c = ISketchColorConstants.CONDITION_FG;
bc = ISketchColorConstants.CONDITION_FG;
break;
case ASTNode.WHILE_STATEMENT:
if (((WhileStatement)currentParent).getExpression() != null) {
expressionString = ((WhileStatement)currentParent).getExpression().toString();
}
text = "while (" +expressionString + ")";
block = currentParent;
c = ISketchColorConstants.LOOP_FG;
bc = ISketchColorConstants.LOOP_BG;
break;
case ASTNode.FOR_STATEMENT:
if (((ForStatement)currentParent).getExpression() != null) {
expressionString = ((ForStatement)currentParent).getExpression().toString();
} else {
expressionString = ";;";
}
text = "for (" + expressionString + ")";
block = currentParent;
c = ISketchColorConstants.LOOP_FG;
bc = ISketchColorConstants.LOOP_BG;
break;
case ASTNode.TRY_STATEMENT:
text = "try";
block = currentParent;
c = ISketchColorConstants.ERROR_FG;
bc = ISketchColorConstants.ERROR_BG;
break;
case ASTNode.CATCH_CLAUSE:
text = "catch (" +((CatchClause)currentParent).getException().toString() +")";
block = currentParent;
c = ISketchColorConstants.ERROR_FG;
bc = ISketchColorConstants.ERROR_BG;
break;
case ASTNode.DO_STATEMENT:
text = "do while (" + ((DoStatement)currentParent).getExpression().toString() + ")";
block = currentParent;
c = ISketchColorConstants.LOOP_FG;
bc = ISketchColorConstants.LOOP_BG;
break;
}
if (text != null) {
MappedMessageGrouping grouping = groups.get(block);
if (grouping == null) {
grouping = new MappedMessageGrouping(activationElement, i, 1, text, block);
grouping.setBackground(bc);
grouping.setForeground(c);
groups.put(block, grouping);
} else {
int length = (i - grouping.getOffset()) + 1;
grouping.setLength(length);
}
}
currentParent = currentParent.getParent();
}
}
}
}
ArrayList<MappedMessageGrouping> groupList = new ArrayList<MappedMessageGrouping>(groups.values());
Collections.sort(groupList, new Comparator<MappedMessageGrouping>(){
public int compare(MappedMessageGrouping o1, MappedMessageGrouping o2) {
ASTNode n1 = (ASTNode) o1.getKey();
ASTNode n2 = (ASTNode) o2.getKey();
int diff = n1.getStartPosition() - n2.getStartPosition();
if (diff == 0) {
diff = (n1.getStartPosition()+n1.getLength())-(n2.getStartPosition()+n2.getLength());
}
return diff;
}
});
return groupList.toArray(new IMessageGrouping[groupList.size()]);
}
public IMessageGrouping[] calculateGroups(UMLSequenceViewer viewer,
Object activationElement, Object[] children) {
HashMap<ASTNode, MappedMessageGrouping> groups = new HashMap<ASTNode, MappedMessageGrouping>();
if (!(activationElement instanceof IAdaptable)) {
return new IMessageGrouping[0];
}
ASTNode activationNode = (ASTNode)((IAdaptable)activationElement).getAdapter(ASTNode.class);
if (!(activationNode instanceof MethodDeclaration)) {
return new IMessageGrouping[0];
}
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof IAdaptable) {
ASTNode messageNode = (ASTNode)((IAdaptable)children[i]).getAdapter(ASTNode.class);
if (messageNode != null) {
ASTNode blockParent = findBlockParent(messageNode);
List<MappedMessageGrouping> blocks = new LinkedList<MappedMessageGrouping>();
while (blockParent != null && blockParent.getNodeType() != ASTNode.METHOD_DECLARATION) {
if (blockParent!= null && blockParent.getNodeType() == ASTNode.IF_STATEMENT) {
IfStatement ifStatement = (IfStatement) blockParent;
ASTNode block = checkIfSide(ifStatement, messageNode);
if (block != null && block.equals(ifStatement.getElseStatement())) {
//add a block for the else statement as well
MappedMessageGrouping blockNode = groups.get(block);
if (blockNode == null) {
blockNode = new MappedMessageGrouping(activationElement, i, 0, "", block);
groups.put(block, blockNode);
}
blocks.add(blockNode);
}
}
MappedMessageGrouping blockNode = groups.get(blockParent);
if (blockNode == null) {
blockNode = new MappedMessageGrouping(activationElement, i, 0, "", blockParent);
groups.put(blockParent, blockNode);
}
blocks.add(blockNode);
blockParent = findBlockParent(blockParent);
}
for (MappedMessageGrouping blockNode : blocks) {
blockNode.setLength(blockNode.getLength()+1);
}
}
}
}
ArrayList<MappedMessageGrouping> groupList = new ArrayList<MappedMessageGrouping>(groups.values());
Collections.sort(groupList, new Comparator<MappedMessageGrouping>(){
public int compare(MappedMessageGrouping o1, MappedMessageGrouping o2) {
ASTNode n1 = (ASTNode) o1.getKey();
ASTNode n2 = (ASTNode) o2.getKey();
int diff = n1.getStartPosition() - n2.getStartPosition();
if (diff == 0) {
diff = (n1.getStartPosition()+n1.getLength())-(n2.getStartPosition()+n2.getLength());
} if (diff == 0) {
IfStatement ifStatement = null;
//make sure that else statements are contained in if statements
if (n1 instanceof IfStatement) {
ifStatement = (IfStatement) n1;
} else if (n2 instanceof IfStatement) {
ifStatement = (IfStatement) n2;
}
if (ifStatement != null) {
if (n2.equals(ifStatement.getElseStatement())) {
return -1;
} else if (n1.equals(ifStatement.getElseStatement())) {
return 1;
}
}
}
return diff;
}
});
for (MappedMessageGrouping blockNode : groupList) {
updateGrouping(blockNode);
}
return groupList.toArray(new IMessageGrouping[groupList.size()]);
}
//[[1,10], [1,2], [2,6], [2,6], [5,6], [6,9], [6,8], [6,10]]
/**
* Updates labels and colours for the grouping.
* @param currentGrouping
*/
private void updateGrouping(MappedMessageGrouping grouping) {
ASTNode node = (ASTNode) grouping.getKey();
String text = "";
int i;
Color bg = null;
Color fg = null;
switch (node.getNodeType()) {
case ASTNode.IF_STATEMENT:
IfStatement ifStatement = (IfStatement) node;
text = "if (" + ifStatement.getExpression().toString() + ")";
//it could be an else-if, make sure
if (ifStatement.getParent().getNodeType() == ASTNode.IF_STATEMENT) {
if (ifStatement.equals(((IfStatement)ifStatement.getParent()).getElseStatement())) {
text = "else " + text;
}
}
fg = ISketchColorConstants.CONDITION_FG;
bg = ISketchColorConstants.CONDITION_BG;
break;
case ASTNode.WHILE_STATEMENT:
WhileStatement whileStatement = (WhileStatement) node;
text = "while (" + whileStatement.getExpression().toString() + ")";
fg = ISketchColorConstants.LOOP_FG;
bg = ISketchColorConstants.LOOP_BG;
break;
case ASTNode.DO_STATEMENT:
DoStatement doStatement = (DoStatement) node;
text = "do..while (" + doStatement.getExpression().toString() + ")";
fg = ISketchColorConstants.LOOP_FG;
bg = ISketchColorConstants.LOOP_BG;
break;
case ASTNode.FOR_STATEMENT:
ForStatement forStatement = (ForStatement) node;
List<?> initializers = forStatement.initializers();
List<?> updaters = forStatement.updaters();
text = "for (";
for (i=0; i < initializers.size(); i++) {
text += initializers.get(i).toString();
if (i < initializers.size()-1) {
text += ",";
}
}
text += ";";
if (forStatement.getExpression() != null) {
text += forStatement.getExpression();
}
text += ";";
for (i = 0; i < updaters.size(); i++) {
text += updaters.get(i).toString();
if (i < updaters.size()-1) {
text += ",";
}
}
text += ")";
fg = ISketchColorConstants.LOOP_FG;
bg = ISketchColorConstants.LOOP_BG;
break;
case ASTNode.ENHANCED_FOR_STATEMENT:
EnhancedForStatement enhancedForStatement = (EnhancedForStatement) node;
text = "for (" + enhancedForStatement.getExpression().toString() + ")";
fg = ISketchColorConstants.LOOP_FG;
bg = ISketchColorConstants.LOOP_BG;
break;
case ASTNode.TRY_STATEMENT:
text = "try";
fg = ISketchColorConstants.ERROR_FG;
bg = ISketchColorConstants.ERROR_BG;
break;
case ASTNode.CATCH_CLAUSE:
CatchClause catchClause = (CatchClause) node;
text = "catch (" + catchClause.getException().toString() + ")";
fg = ISketchColorConstants.ERROR_FG;
bg = ISketchColorConstants.ERROR_BG;
break;
default:
//get the else blocks
if (node instanceof Statement) {
Statement statement = (Statement) node;
if (statement.getParent() instanceof IfStatement) {
if (((IfStatement)statement.getParent()).getElseStatement() == statement) {
text = "else";
fg = ISketchColorConstants.CONDITION_FG;
bg = ISketchColorConstants.CONDITION_BG;
}
}
}
break;
}
grouping.setName(text);
grouping.setForeground(fg);
grouping.setBackground(bg);
}
/**
* @param messageNode
* @return
*/
private static ASTNode findBlockParent(ASTNode messageNode) {
if (messageNode.getNodeType() == ASTNode.METHOD_DECLARATION) {
return messageNode;
}
//search through the tree, up through the parents to find the nearest block
ASTNode parent = messageNode.getParent();
while (parent != null) {
switch (parent.getNodeType()) {
case ASTNode.IF_STATEMENT:
case ASTNode.WHILE_STATEMENT:
case ASTNode.FOR_STATEMENT:
case ASTNode.DO_STATEMENT:
case ASTNode.ENHANCED_FOR_STATEMENT:
case ASTNode.TRY_STATEMENT:
case ASTNode.CATCH_CLAUSE:
case ASTNode.METHOD_DECLARATION:
return parent;
default:
//get the else blocks
if (parent instanceof Statement) {
Statement statement = (Statement) parent;
if (statement.getParent() instanceof IfStatement) {
if (((IfStatement)statement.getParent()).getElseStatement() == statement) {
return statement;
}
}
}
break;
}
parent = parent.getParent();
}
return null;
}
/**
* @param currentParent
* @param messageNode
* @return
*/
private ASTNode checkIfSide(IfStatement ifStatement, ASTNode messageNode) {
ASTNode currentParent = messageNode;
ASTNode thenStatement = ifStatement.getThenStatement();
ASTNode elseStatement = ifStatement.getElseStatement();
while (currentParent != null && currentParent != ifStatement) {
if (currentParent == thenStatement) {
return ifStatement;
} else if (currentParent == elseStatement) {
return elseStatement;
}
currentParent = currentParent.getParent();
}
return null;
}
private boolean contains(ASTNode parent, ASTNode child) {
ASTNode node = child;
while (node != null) {
if (parent == node) {
return true;
}
node = node.getParent();
}
return false;
}
/* (non-Javadoc)
* @see org.eclipse.zest.custom.uml.viewers.IMessageGrouper#dispose()
*/
@Override
public void dispose() {
}
}